/**
 * IconManager.java 21 juil. 07
 *
 * Copyright (c) 2007 windu.2b <jcaddie@windu2b.org>. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.windu2b.jcaddie.swing;

import java.beans.*;
import java.util.*;

import javax.swing.*;
import javax.swing.undo.*;

import org.windu2b.jcaddie.model.*;
import org.windu2b.jcaddie.model.Caddie.*;

/**
 * @author windu.2b
 * 
 */
public class CoursesController
{
	private JComponent	          coursesView;


	private Caddie	              caddie;


	private UserPreferences	      preferences;


	private UndoableEditSupport	  undoSupport;


	private ResourceBundle	      resource;


	private SelectionListener	  selectionListener;


	private PropertyChangeSupport	propertyChangeSupport;




	public CoursesController( Caddie caddie, UserPreferences preferences,
	        UndoableEditSupport undoSupport )
	{
		this.caddie = caddie;
		this.preferences = preferences;
		this.undoSupport = undoSupport;
		// coursesView = new ProductTable( caddie, preferences, this );
	}




	/**
	 * Controls the modification of selected product.
	 */
	public void modifySelectedProduct()
	{
		if ( !Caddie.getProductSubList( caddie.getSelectedItems() ).isEmpty() )
		{
			new CaddieProductController( caddie, preferences, this.undoSupport );
		}
	}




	/**
	 * Returns the view associated with this controller.
	 */
	public JComponent getView()
	{
		return this.coursesView;
	}




	/**
	 * Adds the property change <code>listener</code> in parameter to this
	 * caddie.
	 */
	public void addPropertyChangeListener( Property property,
	        PropertyChangeListener listener )
	{
		this.propertyChangeSupport.addPropertyChangeListener( property
		        .toString(), listener );
	}




	/**
	 * Removes the property change <code>listener</code> in parameter from
	 * this caddie.
	 */
	public void removePropertyChangeListener( Property property,
	        PropertyChangeListener listener )
	{
		this.propertyChangeSupport.removePropertyChangeListener( property
		        .toString(), listener );
	}




	/**
	 * Supprime les <code>items</code> dans la liste et enregistre cela comme
	 * une opération annulable.
	 */
	public void deleteItems( List<? extends Object> items )
	{
		if ( !items.isEmpty() )
		{
			// First post to undo support that walls are deleted,
			// otherwise data about joined walls can't be stored
			postDeleteItems( items );
			// Then delete walls from plan
			for ( Object item : items )
			{
				if ( item instanceof CaddiePieceOfProduct )
				{
					this.caddie
					        .deletePieceOfProduct( ( CaddiePieceOfProduct ) item );
				}
			}
		}
	}




	/**
	 * Moves and shows selected items in plan component of (<code>dx</code>,
	 * <code>dy</code>) units and record it as undoable operation.
	 */
	private void moveAndShowSelectedItems( float dx, float dy )
	{
		List<Object> selectedItems = this.caddie.getSelectedItems();
		if ( !selectedItems.isEmpty() )
		{
			moveItems( selectedItems, dx, dy );
			( ( CoursesComponent ) getView() ).makeSelectionVisible();
			postItemsMove( selectedItems, dx, dy );
		}
	}




	/**
	 * Moves <code>items</code> of (<code>dx</code>, <code>dy</code>)
	 * units.
	 */
	public void moveItems( List<? extends Object> items, float dx, float dy )
	{
		for ( Object item : items )
		{
			if ( item instanceof CaddiePieceOfProduct )
			{
				CaddiePieceOfProduct piece = ( CaddiePieceOfProduct ) item;
				/*
				 * this.caddie.setPieceOfProductLocation( piece, piece.getX() +
				 * dx, piece.getY() + dy );
				 */
			}
		}
	}




	/**
	 * Selects <code>items</code> and make them visible at screen.
	 */
	public void selectAndShowItems( List<? extends Object> items )
	{
		selectItems( items );
		( ( CoursesComponent ) getView() ).makeSelectionVisible();
	}




	/**
	 * Selects <code>items</code>.
	 */
	private void selectItems( List<? extends Object> items )
	{
		// Remove selectionListener when selection is done from this controller
		// to control when selection should be made visible
		this.caddie.removeSelectionListener( this.selectionListener );
		this.caddie.setSelectedItems( items );
		this.caddie.addSelectionListener( this.selectionListener );
	}




	/**
	 * Selects only a given <code>item</code>.
	 */
	private void selectItem( Object item )
	{
		selectItems( Arrays.asList( new Object[] { item } ) );
	}




	/**
	 * Deselects all walls in plan.
	 */
	private void deselectAll()
	{
		selectItems( Collections.emptyList() );
	}




	/**
	 * Posts an undoable delete items operation about <code>deletedItems</code>.
	 */
	private void postDeleteItems( final List<? extends Object> deletedItems )
	{
		// Manage walls
		/*
		 * List<Wall> deletedWalls = new ArrayList<Wall>(); for ( Object item :
		 * deletedItems ) { if ( item instanceof Wall ) { deletedWalls.add( (
		 * Wall ) item ); } }
		 */

		// Manage product
		List<CaddiePieceOfProduct> caddieFurniture = this.caddie.getProduct();
		// Sort the selected product in the ascending order of their index in
		// caddie
		Map<Integer, CaddiePieceOfProduct> sortedMap = new TreeMap<Integer, CaddiePieceOfProduct>();
		for ( Object item : deletedItems )
		{
			if ( item instanceof CaddiePieceOfProduct )
			{
				CaddiePieceOfProduct piece = ( CaddiePieceOfProduct ) item;
				sortedMap.put( caddieFurniture.indexOf( piece ), piece );
			}
		}
		final CaddiePieceOfProduct[] product = sortedMap.values().toArray(
		        new CaddiePieceOfProduct[sortedMap.size()] );
		final int[] productIndex = new int[product.length];
		int i = 0;
		for ( int index : sortedMap.keySet() )
		{
			productIndex[i++] = index;
		}

		UndoableEdit undoableEdit = new AbstractUndoableEdit()
		{
			/**
			 * 
			 */
			private static final long	serialVersionUID	= 0L;




			@Override
			public void undo() throws CannotUndoException
			{
				super.undo();
				doAddProduct( product, productIndex );
				selectAndShowItems( deletedItems );
			}




			@Override
			public void redo() throws CannotRedoException
			{
				super.redo();
				selectItems( deletedItems );
				doDeleteProduct( product );
			}




			@Override
			public String getPresentationName()
			{
				return resource.getString( "undoDeleteSelectionName" );
			}
		};
		this.undoSupport.postEdit( undoableEdit );
	}




	/**
	 * Adds product to caddie at the given indices.
	 */
	private void doAddProduct( CaddiePieceOfProduct[] product,
	        int[] productIndex )
	{
		for ( int i = 0; i < productIndex.length; i++ )
		{
			this.caddie.addPieceOfProduct( product[i], productIndex[i] );
		}
	}




	/**
	 * Deletes product from caddie.
	 */
	private void doDeleteProduct( CaddiePieceOfProduct[] product )
	{
		for ( CaddiePieceOfProduct piece : product )
		{
			this.caddie.deletePieceOfProduct( piece );
		}
	}




	/**
	 * Posts an undoable operation of a (<code>dx</code>, <code>dy</code>)
	 * move of <code>movedItems</code>.
	 */
	private void postItemsMove( List<? extends Object> movedItems,
	        final float dx, final float dy )
	{
		if ( dx != 0 || dy != 0 )
		{
			// Store the moved walls in an array
			final Object[] itemsArray = movedItems
			        .toArray( new Object[movedItems.size()] );
			UndoableEdit undoableEdit = new AbstractUndoableEdit()
			{
				/**
				 * 
				 */
				private static final long	serialVersionUID	= -2890333073129281313L;




				@Override
				public void undo() throws CannotUndoException
				{
					super.undo();
					doMoveAndShowItems( itemsArray, -dx, -dy );
				}




				@Override
				public void redo() throws CannotRedoException
				{
					super.redo();
					doMoveAndShowItems( itemsArray, dx, dy );
				}




				@Override
				public String getPresentationName()
				{
					return resource.getString( "undoMoveSelectionName" );
				}
			};
			this.undoSupport.postEdit( undoableEdit );
		}
	}




	/**
	 * Moves <code>movedItems</code> of (<code>dx</code>, <code>dy</code>)
	 * pixels, selects them and make them visible.
	 */
	private void doMoveAndShowItems( Object[] movedItems, float dx, float dy )
	{
		List<Object> itemsList = Arrays.asList( movedItems );
		moveItems( itemsList, dx, dy );
		selectAndShowItems( itemsList );
	}




	public void setSelectedProduct( List<CaddiePieceOfProduct> selectedProduct )
	{
		caddie.setSelectedItems( selectedProduct );
	}




	/**
	 * 
	 */
	public void selectAll()
	{

	}




	/**
	 * Controls the sort of the product in caddie. If caddie product isn't
	 * sorted or is sorted on an other property, it will be sorted on the given
	 * <code>productProperty</code> in ascending order. If caddie product is
	 * already sorted on the given
	 * <code>productProperty<code>, it will be sorted in descending
	 * order, if the sort is in ascending order, otherwise it won't be sorted at all
	 * and caddie product will be listed in insertion order.
	 * @param productProperty  the product property on which the view wants
	 *          to sort the product it displays.
	 */
	public void sortProduct(
	        CaddiePieceOfProduct.SortableProperty productProperty )
	{
		// Compute sort algorithm described in javadoc
		final CaddiePieceOfProduct.SortableProperty oldProperty = caddie
		        .getProductSortedProperty();
		final boolean oldDescending = caddie.isProductDescendingSorted();
		boolean descending = false;
		if ( productProperty.equals( oldProperty ) )
		{
			if ( oldDescending )
			{
				productProperty = null;
			}
			else
			{
				descending = true;
			}
		}
		caddie.setProductSortedProperty( productProperty );
		caddie.setProductDescendingSorted( descending );
	}




	/**
	 * 
	 */
	public void deleteSelection()
	{

	}




	/**
	 * @return La valeur totale des produits contenus dans le caddie
	 */
	public String getTotalValue()
	{
		float total = 0;
		for ( CaddiePieceOfProduct piece : caddie.getProduct() )
		{
			total += piece.getTotalPrice();
		}
		return new Float( total ).toString();
	}
}